/**
 * Copyright (c) 2010, iDA MediaFoundry
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of iDA MediaFoundry nor the names of its contributors may
 *   be used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package be.idamf.flash.events
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;

	/**
	 *  EventDispatcher that allows removal of all event listeners.
	 * 
	 *  @see be.idamf.flash.events.REventDispatcher
	 *  
	 *  @author Jan Van Coppenolle
	 */
	public class REventDispatcher implements IREventDispatcher
	{
		/**
		 * @private
		 */
		protected var dispatcher:EventDispatcher;

		/**
		 * @private
		 */
		protected var listeners:Array;

		////////////////////////////////////////////////////////////////////////
		// Constructor

		/**
		 *  Constructor.
		 *
		 *  <p>Constructs an instance of an event dispatcher that is able
		 *  to dispose of all listener references with one method call.
		 *  Advised in situations where memory leaks may otherwise arise...</p>
		 */
		public function REventDispatcher(target:IEventDispatcher = null)
		{
			dispatcher = new EventDispatcher(target || this);
			listeners = [];
		}

		////////////////////////////////////////////////////////////////////////
		// IREventDispatcher impl

		/**
		 * Adds a weak event listener.
		 * 
		 * <p>This is the only legal method, adding a weak listeners using the
		 * weak flag in an addEventListener call will result in an error being
		 * thrown.</p>
		 * 
		 * @param type Name of the event.
		 * @param listenerHost References the object that has the listener method.
		 * @param listenerMethod The name of the listener method.
		 */
		public function addWeakEventListener(type:String, listenerHost:Object, listenerMethod:String, useCapture:Boolean = false, priority:int = 0):void
		{
			dispatcher.addEventListener(type, listenerHost[listenerMethod], useCapture, priority, true);
			listeners.push(new EventListener(type, null, useCapture, listenerHost, listenerMethod));
		}

		/**
		 * Removes all event listeners.
		 * 
		 * <p>By calling this method all weak and non-weak listener references will
		 * be removed from this instance.</p>
		 */
		public function removeAllEventListeners():void
		{
			var eventListener:EventListener;

			while (listeners.length != 0)
			{
				eventListener = listeners.pop();
				
				with (eventListener)
				{
					if (listener != null)
						dispatcher.removeEventListener(type, listener, useCapture);
	
					dispose();
				}
				
				eventListener = null;
			}
		}

		////////////////////////////////////////////////////////////////////////
		// IEventDispatcher impl

		/**
		 * Adds an event listener.
		 * 
		 * @throws An exception when useWeakReference is true. To add a weak
		 * event listener the <pre>addWeakEventListener</pre> method should
		 * be used.
		 */
		public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
		{
			if (useWeakReference)
			{
				throw new Error("Wrong method to add a weakly referenced event listener (not removeable) - use addWeakEventListener instead.");
			}

			dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
			listeners.push(new EventListener(type, listener, useCapture));
		}

		public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
		{
			var eventListener:EventListener;
			var n:int = listeners.length;

			for (; n != 0; --n)
			{
				eventListener = listeners[n - 1];

				if (eventListener.listener == null || (type == eventListener.type && listener == eventListener.listener && useCapture == eventListener.useCapture))
				{
					listeners.splice(n - 1, 1);
					eventListener.dispose();
					eventListener = null;
				}
			}

			dispatcher.removeEventListener(type, listener, useCapture);
		}

		public function dispatchEvent(event:Event):Boolean
		{
			return dispatcher.dispatchEvent(event);
		}

		public function hasEventListener(type:String):Boolean
		{
			return dispatcher.hasEventListener(type);
		}

		public function willTrigger(type:String):Boolean
		{
			return dispatcher.willTrigger(type);
		}
	}
}

import flash.utils.Dictionary;

internal class EventListener
{
	private var _listener:Function;
	private var _listenerRef:Dictionary = new Dictionary(true);

	public var type:String;
	public var useCapture:Boolean;

	public function EventListener(type:String, listener:Function, useCapture:Boolean, listenerHost:Object = null, listenerMethod:String = null)
	{
		if (listenerHost)
			_listenerRef[listenerHost] = listenerMethod;
		else
			_listener = listener;

		this.type = type;
		this.useCapture = useCapture;
	}

	public function get listener():Function
	{
		var ref:*;

		for (ref in _listenerRef)
			;

		if (ref == undefined)
			return _listener;

		return ref[_listenerRef[ref]];
	}

	public function dispose():void
	{
		var ref:*;

		_listener = null;

		for (ref in _listenerRef)
			;

		if (ref != undefined)
			delete _listenerRef[ref];

		_listenerRef = null;
	}
}